/**
 * @Author       : Hejh(3010733382@qq.com)
 * @Version      : V1.0
 * @Date         : 2024-01-15 18:55:18
 * @Description  : 常见数据库字段验证的汇总
 */

const { Op, Transaction } = require('sequelize')
const { ERROR_USER_CODE } = require('@/constants/errorCode')
const {
  User,
  Department,
  Role,
  Permission,
  UserDepartment,
  Notice,
  ScheduleTask,
  Source,
  BinSource
} = require('@/models/model')
const R = require('@/models/Response')
const Mysequelize = require('../models')

/**
 * @author: Hejh(3010733382@qq.com)
 * @description: 根据模型的不同返回新的函数，验证模型ID数组合法性
 * @param {*} model 数据库实体模型
 * @returns {Function} 新的函数，验证模型ID数组合法性
 */
const idsCheck = (model) => {
  return async (ids, options = []) => {
    let newIds = [...new Set(ids)]
    // 不能出现重复的
    if (newIds.length !== ids.length) {
      return false
    }

    if (newIds.length === 0) {
      return true
    }

    newIds = newIds.map((item) => BigInt(item))

    // 查看不重复的和数据库的是否对的上
    const myWhere = {
      id: {
        [Op.in]: newIds
      }
    }
    for (let i = 0; i < options.length; i++) {
      let opt = options[i]
      myWhere[opt.field] = opt.value
    }

    const idModels = await model.findAll({
      where: myWhere
    })

    if (idModels.length !== newIds.length) {
      return false
    }

    return true
  }
}

/**
 * @author: Hejh(3010733382@qq.com)
 * @description: 根据模型的不同返回新的函数，验证模型名称数组合法性
 * @param {*} model 数据库实体模型
 * @param {string} fieldName 要验证的名称字段,默认为'name'
 * @returns {Function} 新的函数，验证名称数组的合法性
 */
const nameCheck = (model, fieldName = 'name') => {
  return async (name) => {
    const nameValidate = await model.findOne({
      where: {
        [fieldName]: name
      }
    })
    if (nameValidate) {
      return false
    }
    return true
  }
}

const dumpCheck = {
  /**
   * 检验filename是否重复
   * @param {*} ctx
   * @param {string} parentId 父级资源ID
   * @param {string} filename 文件名称
   * @returns {Promise<boolean>}
   */
  checkFilename: async (ctx, parentId, filename) => {
    // 读未提交事务
    const t = await Mysequelize.transaction({
      isolationLevel: Transaction.ISOLATION_LEVELS.READ_UNCOMMITTED
    })
    const source = await Source.findOne({
      where: {
        parentId: BigInt(parentId),
        filename,
        deleteFlag: 0
      },
      attributes: ['id'],
      transaction: t
    })

    // 说明文件名已经存在
    if (source) {
      return false
    }

    return true
  },

  /**
   * 校验用户名是否重复
   * @param {*} ctx
   * @param {string} username
   * @returns {Promise<boolean>}
   */
  checkUsername: async (ctx, username) => {
    if (!username) {
      return true
    }

    const usernameValidate = await User.findOne({
      where: {
        username
      },
      attributes: ['id']
    })
    if (usernameValidate) {
      ctx.body = R.error(ERROR_USER_CODE.USERNAME_DUMP_ERROR)
      return false
    }

    return true
  },

  /**
   * 检验手机号是否被占用
   * @param {*} ctx
   * @param {string} phone
   * @returns {Promise<boolean>}
   */
  checkPhone: async (ctx, phone) => {
    if (!phone) {
      return true
    }

    const user = await User.findOne({
      where: {
        phone
      },
      attributes: ['id']
    })
    if (user) {
      ctx.body = R.error(ERROR_USER_CODE.PHONE_DUMP_ERROR)
      return false
    }

    return true
  },

  /**
   * 检验邮箱是否被占用
   * @param {*} ctx
   * @param {string} email
   * @returns {Promise<boolean>}
   */
  checkEmail: async (ctx, email) => {
    if (!email) {
      return true
    }

    const user = await User.findOne({
      where: {
        email
      },
      attributes: ['id']
    })
    if (user) {
      ctx.body = R.error(ERROR_USER_CODE.EMAIL_DUMP_ERROR)
      return false
    }

    return true
  },

  /**
   * @author: Hejh(3010733382@qq.com)
   * @description: 验证部门ID的合法性
   * @param {array<string>} depIds 部门ID数组
   * @param {boolean} [ingoreRootId=true] 是否忽略-1根部门ID,默认为true
   * @returns {Promise<boolean>}
   */
  checkDepPids: async (depIds, ingoreRootId = false) => {
    let newdepIds = [...new Set(depIds)]
    // 不能出现重复的
    if (newdepIds.length !== depIds.length) {
      return false
    }
    // 将-1的筛选出来
    if (ingoreRootId) {
      newdepIds = newdepIds.filter((item) => +item !== -1)
    }
    if (newdepIds.length === 0) {
      return true
    }
    newdepIds = newdepIds.map((item) => BigInt(item))

    // 查看不重复的和数据库的是否对的上
    const deps = await Department.findAll({
      where: {
        id: {
          [Op.in]: newdepIds
        }
      },
      attributes: ['id']
    })
    if (deps.length !== newdepIds.length) {
      return false
    }

    return true
  },

  /**
   * @author: Hejh(3010733382@qq.com)
   * @description: 验证角色ID的合法性
   * @param {array<string>} roleIds 角色ID数组
   * @param {boolean} [ingoreSystem=false] 忽略系统预设角色,默认false
   * @param {boolean} [ingoreVisible=true] 忽略visible，默认true
   * @param {0,1} [type=undefined] 角色类型，0-系统，1-文档，默认undefined
   * @returns {Promise<boolean>}
   */
  checkRoleIds: async (
    roleIds,
    ingoreSystem = false,
    ingoreVisible = true,
    type = undefined
  ) => {
    let newroleIds = [...new Set(roleIds)]
    // 不能出现重复的
    if (newroleIds.length !== roleIds.length) {
      return false
    }

    if (newroleIds.length === 0) {
      return true
    }

    newroleIds = newroleIds.map((item) => BigInt(item))

    // 查看不重复的和数据库的是否对的上
    const myWhere = {
      id: {
        [Op.in]: newroleIds
      }
    }
    if (!ingoreSystem) {
      myWhere.system = 0
    }
    if (!ingoreVisible) {
      myWhere.isVisible = 1
    }
    if (type !== undefined) {
      myWhere.type = type
    }
    const roles = await Role.findAll({
      where: myWhere
    })
    if (roles.length !== newroleIds.length) {
      return false
    }

    return true
  },

  /**
   * @author: Hejh(3010733382@qq.com)
   * @description: 验证权限ID是否正确
   * @param {array<string>} permissionIds 权限ID数组
   * @returns {Promise<boolean>}
   */
  checkPermissionIds: idsCheck(Permission),

  /**
   * @author: Hejh(3010733382@qq.com)
   * @description: 用户-部门ID的合法性校验
   * @param {array<string>} userDepIds 用户-部门ID数组
   * @returns {Promise<boolean>}
   */
  checkUserDepIds: idsCheck(UserDepartment),

  /**
   * @author: Hejh(3010733382@qq.com)
   * @description: 用户ID的合法性校验
   * @param {array<string>} userIds 用户ID数组
   * @returns {Promise<boolean>}
   */
  checkUserIds: idsCheck(User),

  /**
   * @author: Hejh(3010733382@qq.com)
   * @description: 验证通知ID数组的合法性
   * @param {array<string>} noticeIds 通知ID数组
   * @returns {Promise<boolean>}
   */
  checkNoticeIds: idsCheck(Notice),

  /**
   * @author: Hejh(3010733382@qq.com)
   * @description: 验证计划任务ID数组的合法性
   * @param {array<string>} scheduleTaskIds 计划任务ID数组
   * @returns {Promise<boolean>}
   */
  checkScheduleTaskIds: idsCheck(ScheduleTask),

  /**
   * @author: Hejh(3010733382@qq.com)
   * @description: 验证计划任务名称的合法性
   * @param {string} name 计划任务名称
   * @returns {Promise<boolean>}
   */
  checkScheduleTaskName: nameCheck(ScheduleTask),

  /**
   * @author: Hejh(3010733382@qq.com)
   * @description: 验证资源ID的合法性
   * @param {Array<string>} ids 资源ID数组
   * @param {Array} [options=[]] where条件字段
   */
  checkSourceIds: idsCheck(Source),

  /**
   * @author: Hejh(3010733382@qq.com)
   * @description: 验证bin-sourceID的合法性
   * @param {Array<string>} ids bin-sourceID数组
   * @param {Array} [options=[]] where条件字段
   */
  checkBinSourceIds: idsCheck(BinSource),

  /**
   * @author: Hejh(3010733382@qq.com)
   * @description: 验证授权对象的合法性
   * @param {*} ctx
   * @param {*} autoTo 授权对象数组
   */
  checkAuthTo: async (ctx, autoTo) => {
    const roleIds = [...new Set(autoTo.map((item) => item.roleId))]
    const userIds = autoTo
      .filter((item) => +item.targetType === 1)
      .map((item) => item.targetId)
    const depIds = autoTo
      .filter((item) => +item.targetType === 2)
      .map((item) => item.targetId)

    const b3 = await dumpCheck.checkRoleIds(roleIds, true, false, 1)
    if (!b3) {
      ctx.body = R.error(ERROR_USER_CODE.ROLE_ID_ERROR)
      return false
    }
    const b4 = await dumpCheck.checkUserIds(userIds)
    if (!b4) {
      ctx.body = R.error(ERROR_USER_CODE.USER_ID_ERROR)
      return false
    }
    const b5 = await dumpCheck.checkDepPids(depIds, false)
    if (!b5) {
      ctx.body = R.error(ERROR_USER_CODE.DEP_ID_ERROR)
      return false
    }

    return true
  }
}

module.exports.dumpCheck = dumpCheck
